package com.et114.components.xmlparse;

import java.io.*;
import java.util.*;

import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;

import javax.xml.parsers.*; //jaxp.jar
import org.w3c.dom.*;

import com.et114.core.utility.Section;

//(com.sun.xml.util.XmlNames.isName(input));//parser.jar

public class DomUtil {
  static public final short POS_UP = 1;
  static public final short POS_DOWN = 2;
  static public final short POS_TOP = 3;
  static public final short POS_BOTTOM = 4;

  public String xmlFile = null;
  private Document xmlDocument = null;
  public DomUtil(String file) throws Throwable {
//    try{
    File f = null;
    DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.
	newInstance();
    DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
    xmlFile = file;
    f = new File(xmlFile);
    if (!f.isFile()) {
    	DomUtil.newXML(xmlFile, "root");
//        this.createConfigFile( xmlFile );
      f = new File(xmlFile);
    }
    xmlDocument = docBuilder.parse(f);
//    }catch ( Throwable t ){
//      GM.showException( new Exception("��ʼ��XML�ļ���\r\n"+file+"\r\nʧ�ܡ�����ԭ��Ϊ��\r\n"+t.getMessage()) );
//    }
  }

  public DomUtil(File file) throws Throwable {
    DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.
	newInstance();
    DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
    xmlFile = file.getAbsolutePath();
    if (file.exists() && file.isFile()) {
      xmlDocument = docBuilder.parse(file);
    }
    else {
    	DomUtil.newXML(xmlFile, "root");
    }
  }

  /**
   * �����µ�XML�ļ�����false
   */
  static public DomUtil newXML(String file, String root) throws Throwable {
    if (!isLegalXmlName(root)) {
      throw new Exception(file + "::" + root); 
    }

    DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.
	newInstance();
    DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
    Document sxmlDocument = docBuilder.newDocument();
    Element eRoot = sxmlDocument.createElement(root);
    eRoot.normalize();
    sxmlDocument.appendChild(eRoot);

    writeNodeToFile(sxmlDocument, file);
    return new DomUtil(file);
  }

  /**
ء����Ե�Section����
   */
  public Section listAll(String path) throws Exception {
    Section ss = new Section();
    Node tmpNode = getTerminalNode(path, Node.ELEMENT_NODE);
//      if (tmpNode.getNodeType() != Node.ELEMENT_NODE) {
//        throw new Exception("���ڵ㲻��Ԫ�ؽڵ㡣");
//      }
    ss.unionSection(getNodesName(tmpNode.getChildNodes()));
    ss.unionSection(getNodesName(tmpNode.getAttributes()));
    return ss;
  }

  /**
   * �г��ļ�file�е�·��path�µ�������Ԫ��
   * @param path �ڵ�·��
   * @return ���ذ�path�ڵ���Ԫ�ص�Section����
   */
  public Section listElement(String path) throws Exception {
    Node tmpNode = getTerminalNode(path, Node.ELEMENT_NODE);
//      if (tmpNode.getNodeType() != Node.ELEMENT_NODE) {
//        throw new Exception("���ڵ㲻��Ԫ�ؽڵ㡣");
//      }
    return getNodesName(tmpNode.getChildNodes());
  }

  /**
   * �г��ļ�file�е�·��path�µ���������
   * @param path �ڵ�·��
   * @return ���ذ�path�ڵ����������Section����
   */
  public Section listAttribute(String path) throws Exception {
    Node tmpNode = getTerminalNode(path, Node.ELEMENT_NODE);
//      if (tmpNode.getNodeType() != Node.ELEMENT_NODE) {
//        throw new Exception("���ڵ㲻��Ԫ�ؽڵ㡣");
//      }
    return getNodesName(tmpNode.getAttributes());
  }

  /**
   * �г��ļ�file�е�·��path��ָ�����Ե�����ֵ
   * @param path �ڵ�·��
   * @return �����Ե�ֵ
   */
  public String getAttribute(String path) {
    try {
      Node tmpNode = getTerminalNode(path, Node.ATTRIBUTE_NODE);
      return tmpNode.getNodeValue();
    }
    catch (Exception ex) {
      return "";
    }
  }

  /**
   * �ڵ�ǰ��·�����½�Ԫ�أ����ǰ��·����ָ��Ľڵ����������Խڵ�����½�ʧ��
   * @param path �ڵ�·��
   * @param element Ҫ�½���Ԫ�����
   * @return �½�Element����ɹ���ĸýڵ�·��
   */
  public String newElement(String path, String element) throws Exception {
    Element newElement = null;
    if (!isLegalXmlName(element)) {
      throw new Exception(xmlFile + ":"
			  + element);
    }

    Node parent = getTerminalNode(path, Node.ELEMENT_NODE);
    NodeList nl = parent.getChildNodes();
    int i;
    for (i = 0; i < nl.getLength(); i++) {
      if (nl.item(i).getNodeName().equalsIgnoreCase(element)) {
	break;
      }
    }
    if (i >= nl.getLength()) {
      newElement = xmlDocument.createElement(element);
      parent.appendChild(newElement);
    }
    else {
      //The element exists, do nothing
//        newElement = (Element) nl.item(i);
    }
    return path + "/" + element;
  }

  /**
   * �ڵ�ǰ��·�����½����ԣ����ǰ��·����ָ��Ľڵ����������Խڵ�����½�ʧ��
   * @param path �ڵ�·��
   * @param attr Ҫ�½����������
   * @return �ɹ�����1��ʧ���򷵻�-1(����ͬ������ʱ)
   */
  public int newAttribute(String path, String attr) throws Exception {
    if (!isLegalXmlName(attr)) {
      throw new Exception(xmlFile + ":" + attr);
    }

    Node parent = getTerminalNode(path, Node.ELEMENT_NODE);

    NamedNodeMap nl = parent.getAttributes();
    int i;
    for (i = 0; i < nl.getLength(); i++) {
      if (nl.item(i).getNodeName().equalsIgnoreCase(attr)) {
	break;
      }
    }
    if (i >= nl.getLength()) {
      ( (Element) parent).setAttribute(attr, "");
    }
    else {
      return -1;
    }
    return 1;
  }

  /**
   * ɾ����path·��ָ���Ľڵ㣬�ýڵ��(���Խڵ��Ԫ�ؽڵ�
   * @param path �ڵ�·��
   */
  public void delete(String path) throws Exception {
    deleteAttribute(path);
    deleteElement(path);
  }

  /**
   * ɾ����path·��ָ����Ԫ�ؽڵ�
   * @param path �ڵ�·��
   */
  public void deleteElement(String path) throws Exception {
    Node nd = getTerminalNode(path, Node.ELEMENT_NODE);
    Node parent = nd.getParentNode();
    parent.removeChild(nd);
  }

  public boolean isPathExists(String path) {
    try {
      getTerminalNode(path, Node.ELEMENT_NODE);
    }
    catch (Exception ex) {
      return false;
    }
    return true;
  }

  /**
   * ɾ����path·��ָ�������Խڵ�
   * @param path �ڵ�·��
   */
  public void deleteAttribute(String path) throws Exception {
    int i = path.lastIndexOf('/');
    Node parent;
    if (i > 0) {
      String p = path.substring(0, i);
      parent = getTerminalNode(p, Node.ELEMENT_NODE);
    }
    else {
      return;
//        throw new Exception("û���ҵ����ԣ�"+path);
    }
    ( (Element) parent).removeAttribute(path.substring(i + 1)); //Remove attribute
  }

  /**
   * ���path·����ָ������Խڵ�����
   * @param path �ڵ�·��
   * @param newName �µ����
   * @throws Exception
   */
  public void renameAttribute(String path, String newName) throws Exception {
    int i;
    i = path.lastIndexOf('/');
    if (i == -1) {
      throw new Exception(xmlFile + ":" + path);
    }

    String value = getAttribute(path);
    deleteAttribute(path);
    setAttribute(path.substring(0, i) + "/" + newName, value);
  }

  /**
   * ���path·����ָ���Ԫ�ؽڵ�����
   * @param path �ڵ�·��
   * @param newName �µ����
   * @throws Exception
   */
  public void renameElement(String path, String newName) throws Exception {
    if (path.lastIndexOf('/') == -1) {
      throw new Exception(xmlFile + "" );
    }
    Node nd = getTerminalNode(path, Node.ELEMENT_NODE);
    Node pp = nd.getParentNode();
    NodeList childNodes = pp.getChildNodes();
    int i;
    if (childNodes != null) {
      for (i = 0; i < childNodes.getLength(); i++) {
	if (newName.equalsIgnoreCase(childNodes.item(i).getNodeName())) {
	  throw new Exception(xmlFile + ":" +
			      newName);
	}
      }
    }

    Element newNode = xmlDocument.createElement(newName);

    childNodes = nd.getChildNodes();
    if (childNodes != null) {
      for (i = 0; i < childNodes.getLength(); i++) {
	newNode.appendChild(childNodes.item(i));
      }
    }
    NamedNodeMap childAttr = nd.getAttributes();
    Node tmpNode;
    if (childAttr != null) {
      for (i = 0; i < childAttr.getLength(); i++) {
	tmpNode = childAttr.item(i);
	newNode.setAttribute(tmpNode.getNodeName(), tmpNode.getNodeValue());
      }
    }
    pp.replaceChild(newNode, nd);
  }

  /**
   * ����path��ָ�����Խڵ������ֵ����ĩ�����Խڵ���Բ����ڣ����丸�ڵ���������Ϊelement
   * @param path �ڵ�·��
   * @param value Ҫ���õ�����ֵ��null��ɾ�������
   */
  public void setAttribute(String path, String value) throws Exception {
    if (path == null) {
      throw new Exception(xmlFile + "\nNull object of path.");
    }
    if (path.trim().length() == 0) {
      throw new Exception(xmlFile + " ");
    }
    if (value == null) {
      deleteAttribute(path);
      return;
    }
    Node nd;
    int i = path.lastIndexOf('/');
    if (i > 0) {
      String p = path.substring(0, i);
      String v = path.substring(i + 1);
      newAttribute(p, v);
    }

    nd = getTerminalNode(path, Node.ATTRIBUTE_NODE);
    nd.setNodeValue(value);
  }

  /**
-1
   */
  public int setPosition(String path, int posCmd) throws Exception {
    Node nd = getTerminalNode(path, Node.ELEMENT_NODE);
    switch (nd.getNodeType()) {
      case Node.ELEMENT_NODE:
	Node parent = nd.getParentNode();
	if (parent == null) {
	  throw new Exception(xmlFile + "" );
	}
	switch (posCmd) {
	  case POS_TOP:
	    break;
	  case POS_BOTTOM:
	    break;
	  case POS_UP:
	    break;
	  case POS_DOWN:
	    break;
	  default:
	    throw new Exception(xmlFile + "" );
	}

	break;
      case Node.ATTRIBUTE_NODE:
	break;
      default:
	throw new Exception(xmlFile + "" );
    }
    return 1;
  }

  public void save() throws Exception {
    writeNodeToFile(xmlDocument, xmlFile);
  }

  static public void writeNodeToFile(Document node, String file) throws
      Exception {
    TransformerFactory tFactory = TransformerFactory.newInstance();
    Transformer transformer = tFactory.newTransformer();

    //transformer.setOutputProperty("encoding", "gb2312");
    Properties properties = transformer.getOutputProperties();
    properties.setProperty(OutputKeys.ENCODING, "UTF-8");
    properties.setProperty(OutputKeys.METHOD, "xml");
    properties.setProperty(OutputKeys.INDENT, "yes");
    transformer.setOutputProperties(properties);

    DOMSource source = new DOMSource(node);
    StreamResult result = new StreamResult(file);
    transformer.transform(source, result);
  }

  private Section getNodesName(Object obj) throws Exception {
    Section ss = new Section();
    if (obj == null) {
      return ss;
    }

    Node nd = null;
    if (obj instanceof NodeList) {
      NodeList nl = (NodeList) obj;
      for (int i = 0; i < nl.getLength(); i++) {
	nd = nl.item(i);
	if (nd.getNodeType() != Node.ELEMENT_NODE) {
	  continue;
	}
	ss.unionSection(nd.getNodeName());
      }
    }

    if (obj instanceof NamedNodeMap) {
      NamedNodeMap nnm = (NamedNodeMap) obj;
      for (int i = 0; i < nnm.getLength(); i++) {
	nd = nnm.item(i);
	ss.unionSection(nd.getNodeName());
      }
    }
    return ss;
  }

  private Node locateNode(Object obj, String nodeName) throws Exception {
    int i = 0, j = 0;
    String sTmp;
    Node tmpNode = null;
    NodeList nl;
    NamedNodeMap nnm;

    if (obj instanceof NodeList) {
      nl = (NodeList) obj;
      i = nl.getLength();
      for (j = 0; j < i; j++) {
	tmpNode = nl.item(j);
	sTmp = tmpNode.getNodeName();
	if (sTmp.equalsIgnoreCase(nodeName)) {
	  break;
	}
      }
    }
    if (obj instanceof NamedNodeMap) {
      nnm = (NamedNodeMap) obj;
      i = nnm.getLength();
      for (j = 0; j < i; j++) {
	tmpNode = nnm.item(j);
	sTmp = tmpNode.getNodeName();
	if (sTmp.equalsIgnoreCase(nodeName)) {
	  break;
	}
      }
    }
    if (j == i) {
      throw new Exception(xmlFile + "" +
			  nodeName);
    }
    return tmpNode;
  }

  private Node getTerminalNode(String path, short type) throws Exception {
    Node tmpNode = null;
    String sNode, sLast;
    int i;
    if (path == null) {
      throw new Exception(xmlFile );
    }
    i = path.lastIndexOf('/');
    if (i == -1) {
      if (path.length() == 0) {
	return xmlDocument;
      }
      sNode = path;
      if (type == Node.ELEMENT_NODE) {
	tmpNode = locateNode(xmlDocument.getChildNodes(), sNode);
      }
    }
    else {
      sLast = path.substring(i + 1);
      path = path.substring(0, i);

      StringTokenizer st = new StringTokenizer(path, "/");
      NodeList childNodes = xmlDocument.getChildNodes();
      while (st.hasMoreTokens()) {
	sNode = st.nextToken();
	tmpNode = locateNode(childNodes, sNode);
	if (tmpNode == null) {
	  throw new Exception(xmlFile + "" +
			      path);
	}
	childNodes = tmpNode.getChildNodes();
      }
      if (type == Node.ELEMENT_NODE) {
	tmpNode = locateNode(tmpNode.getChildNodes(), sLast);
      }
      else {
	tmpNode = locateNode(tmpNode.getAttributes(), sLast);
      }
    }
    if (tmpNode == null) {
      throw new Exception(xmlFile + "" );
    }
    return tmpNode;
  }

  public static boolean isLegalXmlName(String input) {
    if (input == null || input.length() == 0) {
      return false;
    }
    return (com.sun.xml.util.XmlNames.isName(input));
  }
}
